package com.egao.common.core.security;

import com.alibaba.fastjson.JSON;
import com.egao.common.core.Constants;
import com.egao.common.core.exception.GlobalExceptionHandler;
import com.egao.common.core.web.JsonResult;
import com.egao.common.module.system.entity.LoginRecord;
import com.egao.common.module.system.entity.User;
import com.egao.common.module.system.service.UserService;
import io.jsonwebtoken.Claims;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * 请求过滤器,处理携带token的请求
 *
 * @author system
 */
public class JwtRequestFilter extends OncePerRequestFilter {

    private final UserDetailsService userDetailsService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private UserService userService;

    /**
     * 异常数据
     */
    private final String keyWord = "null,undefined";

    private final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class.getName());

    public JwtRequestFilter(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        String accessToken = JwtUtil.getAccessToken(request);
        // 前端传的null 字符串
        if (accessToken != null && !keyWord.contains(accessToken)) {
            try {
                // 解析TOKEN
                Claims claims = JwtUtil.parseToken(accessToken, Constants.TOKEN_KEY);

                // 获取登录用户名undefined
                String username = claims.getSubject();

                // 查询当前用户Cas登录状态 状态为登出则返回登出结果 （监听cas其他应用退出后 cas登录状态会变为登出状态）
                // 捕获异常 正常执行下面流程
                try {
                    String casLoginState = redisTemplate.opsForValue().get(username);
                    if (Constants.CAS_LOGOUT_MARK.equals(casLoginState)) {
                        response.setContentType("application/json;charset=UTF-8");
                        PrintWriter out = response.getWriter();
                        JsonResult result = JsonResult.error(401, "登录已过期");
                        out.write(JSON.toJSONString(result));
                        out.flush();
                        return;
                    }
                } catch (Exception e) {
                    logger.error("REDIS ERROR", e);
                }

                // 获取用户信息
                User userDetails = userService.getByUsername(username);
                userService.selectUserAuth(userDetails);

                // 重新授权
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                SecurityContextHolder.getContext().setAuthentication(authentication);

                // TOKEN预过期处理 重新签发TOKEN 防止突然登出
                long t = (claims.getExpiration().getTime() - System.currentTimeMillis()) / 1000 / 60;
                if (t < Constants.TOKEN_WILL_EXPIRE) {
                    String access_token_new = JwtUtil.buildToken(username, Constants.TOKEN_EXPIRE_TIME, Constants.TOKEN_KEY);
                    response.addHeader(JwtUtil.TOKEN_HEADER_NAME, access_token_new);
                    // 添加响应头白名单 未添加的白名单无法被前端获取到
                    response.addHeader("Access-Control-Expose-Headers", JwtUtil.TOKEN_HEADER_NAME);
                }
            } catch (Exception e) {
                // 基本是过期解析问题 暂不记录 日志非常多
                // logger.error("JwtRequestFilter", e);
            }
        }
        chain.doFilter(request, response);
    }

}
